<!--
* @Author: zhangjing
* @Date: 2025-05-07 17:19:39
* @Description: 语音测评=>
-->
<template>
	<div class="myForm">
		<audio :src="audioUrl" ref="audioRef"></audio>
		<audio :src="audioUrl2" ref="audioRef2"></audio>
		<div class="btn" v-if="operateType == 'nostart'" @click="print">
			<img src="/home/btn.png" alt="" />
			<span>取词</span>
		</div>
		<template v-if="operateType == 'hasPrint'">
			<div class="flex" style="align-items: flex-end; justify-content: center">
				<div id="audio" v-if="hasAudio" @click="playAudio">
					<div class="audiobox" v-if="play">
						<div id="one"></div>
						<div id="two"></div>
						<div id="three"></div>
						<div id="four"></div>
						<div id="five"></div>
					</div>
					<img src="/audio/audio.png" />
				</div>
				<startBtn
					@startAnswer="startAnswer"
					@startQuickanswer="startQuickanswer"
					:machineData="machineData"
					:formData="formData"
				></startBtn>
			</div>
			<div style="color: #f00">长按语音键2s后开始答题，以防收音不完整</div>
		</template>
		<!-- 结束 -->
		<template v-if="operateType == 'answer'">
			<div class="stopbox flex">
				<div class="stopBtn" @click="stopAnswer">结束</div>
				<div class="resultBtn">{{ progress }}%</div>
			</div>
		</template>
		<template v-if="operateType == 'result'">
			<pannel @reback="reback">
				<a-spin :spinning="spinning" style="height: 100%">
					<div class="list flex flex-wrap">
						<div class="item" v-for="(item, index) in rankList" :key="index">
							<img :src="`/rank/icon1.png`" alt="" />
							<img
								v-if="item.questId"
								:src="`/audio/noticeplay.${item.play ? 'gif' : 'png'}`"
								class="play"
								@click="playStuAudio(item, index)"
							/>
							<div class="score">
								<a-rate :value="item.rate" disabled />
							</div>
							<div>{{ item.studName }}</div>
						</div>
					</div></a-spin
				>
			</pannel>
		</template>
	</div>
</template>

<script>
import startBtn from './startBtn.vue';
import pannel from './pannel.vue';
import { mapState, mapMutations } from 'vuex';
import { getBytesLength, filterOption, debounce, padZero, convertToTimestamp, formatMsToDate } from '@/utils/base.js';
import bus from '@/utils/eventBus';
import { answerTypeEnum } from '@/utils/enumeration.js';
//雪花字符
class Snowflake {
	constructor(workerId, datacenterId, epoch = BigInt(1577836800000)) {
		// 2020-01-01
		if (workerId > 1023n || workerId < 0n) {
			throw new Error('worker Id can only have 10 bits');
		}
		if (datacenterId > 31n || datacenterId < 0n) {
			throw new Error('datacenter Id can only have 5 bits');
		}
		this.workerId = workerId;
		this.datacenterId = datacenterId;
		this.sequence = 0n;
		this.epoch = epoch;
		this.lastTimestamp = -1n;
	}

	nextId() {
		const timestamp = BigInt(Date.now());

		if (timestamp < this.lastTimestamp) {
			throw new Error(
				`Clock moved backwards. Refusing to generate id for ${this.lastTimestamp - timestamp} milliseconds`
			);
		}

		if (timestamp === this.lastTimestamp) {
			this.sequence = (this.sequence + 1n) & 4095n; // 12 bits
			if (this.sequence === 0n) {
				timestamp = this.tilNextMillis(this.lastTimestamp);
			}
		} else {
			this.sequence = 0n;
		}

		this.lastTimestamp = timestamp;

		return ((timestamp - this.epoch) << 22n) | (this.datacenterId << 17n) | (this.workerId << 12n) | this.sequence;
	}

	tilNextMillis(lastTimestamp) {
		let timestamp = BigInt(Date.now());
		while (timestamp <= lastTimestamp) {
			timestamp = BigInt(Date.now());
		}
		return timestamp;
	}
}
const snowflake = new Snowflake(BigInt(1), BigInt(1));
export default {
	data() {
		return {
			evalutionText: '',
			answerstulist: [],
			sendType: '',
			baseUrl: process.env.VUE_APP_BASE_API + '/mp3/',
			operateType: 'nostart',
			bidObj: {},
			answerlist: [],
			hasQuickanswer: false, //是否有学生抢到
			rankList: [],
			play: false,
			audioUrl2: '',
			spinning: false,
			questId: '',
			voiceNum: 0, //发送的语音数量
			voiceTxtNum: 0, //后端处理的语音数量
		};
	},
	components: { startBtn, pannel },
	props: {
		//接收器信息
		machineData: {
			type: Object,
			default: () => {},
		}, //上课数据
		formData: {
			type: Object,
			default: () => {},
		},
	},
	computed: {
		...mapState({
			studentList: (state) => state.websockStore.studentList,
			deviceType: (state) => state.websockStore.deviceType,
			isAnswering: (state) => state.websockStore.isAnswering,
			minProjectLogin: (state) => state.minProjectStore.minProjectLogin, //小程序是否登录
			sendTopicData: (state) => state.websockStore.sendTopicData, //小程序已发试题
			teacherInfo: (state) => state.user.teacherInfo,
			userinfo: (state) => state.user.userinfo,
			ans_id: (state) => state.websockStore.ans_id,
			hasAudio: (state) => state.minProjectStore.hasAudio,
		}),

		progress() {
			return this.answerstulist.length > 0
				? ((this.answerlist.length / this.answerstulist.length) * 100).toFixed(2)
				: 0;
		},
		wiredAnswer() {
			return this.$store.getters.onEvent('wiredanswer');
		},
		//是否是语音设备
		isVoiceDevice() {
			return ['RP554', 'RP555P'].includes(this.machineData.wareVersion);
		},
		voiceStream() {
			return this.$store.getters.onEvent('voice_stream');
		},
		//后端处理mp3后的回调
		cosyvoiceAnswer() {
			return this.$store.getters.onCosyvoiceChar();
		},
		audioUrl() {
			return this.baseUrl + this.questId + '/a.mp3';
		},
		//是否可以统计
		isCanCount() {
			return {
				voiceNum: this.voiceNum,
				voiceTxtNum: this.voiceTxtNum,
				operateType: this.operateType,
			};
		},
	},
	watch: {
		voiceStream: {
			handler(newValue, oldValue) {
				if (newValue && JSON.stringify(newValue) != JSON.stringify(oldValue)) {
					console.log('this.ans_id', this.ans_id);
					if (this.ans_id == newValue.ans_id) {
						console.log('newValue.pac_num', newValue.pac_num);
						if (newValue.pac_num == 1) {
							this.bidObj[newValue.uID] = snowflake.nextId().toString();
							this.voiceNum++;
						}
						//一个语音包结束
						if (newValue.pac_flg == 1) {
							//更新答题进度
							if (this.answerstulist) {
								this.answerstulist.forEach((item) => {
									if (item.machineId - 0 == newValue.uID) {
										item.state = 1;
									}
								});
								this.answerlist =
									this.answerstulist.length > 0
										? this.answerstulist.filter((item) => item.state)
										: [];
							}
						}
						console.log('this.bidObj[newValue.uID]', this.bidObj[newValue.uID]);
						if (this.bidObj[newValue.uID]) {
							let msg = {
								...newValue,
								bid: this.bidObj[newValue.uID],
								csId: this.formData.csId, //课题id
								ans_id: this.questId,
								model: 'qwen',
								volType: newValue.voice_type == 'speex' ? 1 : 0,
								sdQuestId: this.questId,
								subjCode: this.formData.subjectCode,
								questId: this.questId,
							};
							this.que_CPVOICE_SEND(JSON.stringify(msg));
						}
					}
				}
			},
			immediate: true, //初始化时让handler调用一下
			deep: true, // 开启深度监听
		},
		cosyvoiceAnswer: {
			handler(newValue, oldValue) {
				console.log('newValue', newValue, this.ans_id);
				if (newValue && JSON.stringify(newValue) != JSON.stringify(oldValue)) {
					if (this.questId == newValue.sdQuestId) {
						this.voiceTxtNum++;
						if (this.answerstulist) {
							this.answerstulist.forEach((item) => {
								if (item.machineId - 0 == newValue.uID) {
									item.state = 1;
								}
							});
						}
					}
					this.answerlist =
						this.answerstulist.length > 0 ? this.answerstulist.filter((item) => item.state) : [];
				}
			},
			immediate: true, //初始化时让handler调用一下
			deep: true, // 开启深度监听
		},
		wiredAnswer: {
			handler(newValue, oldValue) {
				let beginClassData = this.$ls.get('beginClass');
				if (beginClassData && JSON.stringify(beginClassData) != '{}' && beginClassData.topicType != 99) {
					if (newValue && newValue != oldValue) {
						let uID = newValue.uID - 0;
						let stuInfo = this.studentList.find((item) => item.machineId - 0 == uID);
						if (stuInfo && this.sendType == 'quickanswer' && !this.hasQuickanswer) {
							//如果是抢答，且还没有学生抢到，则抢到的学生发题
							if (
								(this.isVoiceDevice && newValue.ans_str == 'G') ||
								(!this.isVoiceDevice &&
									newValue.ans_str.length > 0 &&
									newValue.ans_str.some((item) => item.type == 'hand'))
							) {
								if (this.isVoiceDevice) {
									this.studentList.forEach((item) => {
										if (item.status == 1) {
											let msg = '   ' + item.studName;
											let len = 16 - getBytesLength(msg);
											for (var i = 0; i < len; i++) {
												msg = msg + ' ';
											}
											if (item.machineId - 0 == uID) {
												msg = msg + '   抢答成功';
											} else {
												msg = msg + '   已有人抢答';
											}
											this.WEBSOCKET_SEND({
												fun: 'dtq_echo_set',
												uID: item.machineId - 0,
												msg: msg,
											});
										}
									});
								}
								this.hasQuickanswer = true;
								bus.$emit('quickanswerEvent', stuInfo);
								setTimeout(() => {
									this.startAnswer([stuInfo.studId], 'quickanswer');
								}, 1000);
							}
							return;
						}
					}
				}
			},
			immediate: true, //初始化时让handler调用一下
			deep: true, // 开启深度监听
		},
		//是否可以触发统计
		isCanCount(newVal) {
			if (newVal.voiceNum == newVal.voiceTxtNum && newVal.operateType == 'result') {
				this.tongjiVoice(this.questId, this.formData.classId);
			}
		},
	},
	created() {
		let that = this;
		if (window.ipcRenderer) {
			window.ipcRenderer.on('clipboardtxt', this.handleClipboardText);
		}
	},
	mounted() {
		let $me = this;
		this.$nextTick(() => {
			var audio = this.$refs.audioRef;
			if (audio) {
				audio.addEventListener(
					'ended',
					function () {
						$me.play = false;
					},
					false
				);
			}
			var audio2 = this.$refs.audioRef2;
			if (audio2) {
				audio2.addEventListener(
					'ended',
					function () {
						$me.rankList.map((item) => {
							item.play = false;
						});
						$me.audioUrl2 = null;
					},
					false
				);
			}
		});
	},
	destroyed() {
		if (window.ipcRenderer) {
			console.log('开始注销成功');
			window.ipcRenderer.off('clipboardtxt', this.handleClipboardText);
		}
	},
	methods: {
		...mapMutations([
			'WEBSOCKET_SEND',
			'SET_isAnswering',
			'que_CPTEXT_SEND',
			'que_CPVOICE_SEND',
			'SET_ANS_ID',
			'audio_RABBITMQ_SEND',
			'CLEARAUDIOURL',
		]),
		//去答题
		startAnswer(selectStuList, type) {
			/* 判断移动端正在控制PC端 */
			if (this.minProjectLogin) {
				this.$message.error('移动端正在控制PC端，不能发题！');
				return;
			}
			//发题前判断该班级是否有学生。
			if (selectStuList.length == 0) {
				let beginClassData = this.$ls.get('beginClass');
				this.$message.error('（' + beginClassData.className + '）班级没有学生，请先添加学生！');
				return;
			}
			if (!this.formData.classId) {
				this.$message.error('请选择作答班级！');
				return;
			}

			/* 校验接收器是否连接 */
			if (!this.machineData || Object.keys(this.machineData).length == 0 || !this.machineData.online) {
				this.$message.error('没有连接接收器');
				return;
			}
			this.answerlist = [];
			this.answerstulist = this.studentList.filter((item) => selectStuList.includes(item.studId));
			this.answerstulist.forEach((item) => {
				item.state = 0;
			});
			this.sendType = type;
			this.sendQuestion();

			console.log('this.answerstulist', this.answerstulist);
		},

		async sendQuestion() {
			this.voiceNum = 0;
			this.voiceTxtNum = 0;
			await this.sendVoiceTest();
			const uidlist = this.answerstulist.map((item) => item.machineId - 0);
			if (this.isVoiceDevice) {
				let msg = {
					fun: 'answer_start',
					uidlist,
					type: 8,
					ans_id: this.ans_id,
				};
				this.WEBSOCKET_SEND(msg);
				//发送回显
				this.answerstulist.forEach((item) => {
					if (item.status == 1) {
						let msg = '   ' + item.studName;
						let len = 16 - getBytesLength(msg);
						for (var i = 0; i < len; i++) {
							msg = msg + ' ';
						}
						msg = msg + ' 长按语音键测评';
						this.WEBSOCKET_SEND({
							fun: 'dtq_echo_set',
							uID: item.machineId - 0,
							msg: msg,
						});
					}
				});
				this.operateType = 'answer';
				this.SET_isAnswering(1);
			}
		},
		//发送语音题
		sendVoiceTest() {
			let { classId, csId } = this.formData;
			return this.$postAction(this.$sysManageapi.answerUrl.sendTempTestPaper, {
				classId,
				csId,
				stuIds: this.answerstulist.map((item) => item.studId),
				teacherId: this.teacher && this.teacher.teacherId,
				type: answerTypeEnum[this.sendType], //全答，选答，抽答
				userId: this.userinfo.userId,
				isVoice: 1, //是否是语音题
			});
		},
		stopAnswer() {
			this.WEBSOCKET_SEND({
				fun: 'answer_start',
				uID: 0,
				type: 128, //结束答题
			});
			this.studentList.forEach((item) => {
				if (item.status == 1) {
					let msg = '   ' + item.studName;
					this.WEBSOCKET_SEND({
						fun: 'dtq_echo_set',
						uID: item.machineId - 0,
						msg: msg,
					});
				}
			});
			this.spinning = true;
			this.rankList = [];
			this.operateType = 'result';
			this.SET_isAnswering(0);
		},
		tongjiVoice(questId, classId) {
			this.$postAction(
				this.$sysManageapi.answerUrl.cptongjiVoice + '?questId=' + this.questId + '&classId=' + classId
			)
				.then((res) => {
					if (res && res.code == 1) {
						if (res.data.similartyVos && res.data.similartyVos.length > 0) {
							res.data.similartyVos.forEach((item) => {
								item.rate = Math.ceil(item.score / 20);
								item.rate = item.rate == 0 ? 1 : item.rate;
								item.play = false;
							});
						}
						this.tongjiVoiceInfo = res.data;
						let list = this.tongjiVoiceInfo.similartyVos.sort((a, b) => {
							return b.score - a.score;
						});
						//查找出没有作答的学生，
						let answerIds = list.map((item) => item.uid);
						let noAnswer = this.answerstulist
							.filter((item) => !answerIds.includes(item.machineId))
							.map((item) => {
								return {
									...item,
									rate: 0,
									score: 0,
									index: -1,
								};
							});
						this.rankList = [...list, ...noAnswer];
					}
				})
				.finally(() => {
					this.spinning = false;
					this.questId = '';
					this.$ls.remove('questId');
					this.SET_ANS_ID('');
					this.CLEARAUDIOURL();
				});
		},
		reback() {
			this.operateType = 'nostart';
		},
		//开始抢答
		startQuickanswer() {
			//是否被抢到
			this.sendType = 'quickanswer';
			this.hasQuickanswer = false;
		},
		playAudio() {
			if (this.play) {
				this.play = false;
				this.$refs.audioRef.pause();
			} else {
				this.play = true;
				this.$refs.audioRef.load();
				this.$nextTick(() => {
					this.$refs.audioRef.play();
				});
			}
		},
		print() {
			if (window.ipcRenderer) {
				this.operateType = 'print';
				window.ipcRenderer.send('PrintImg');
			}
		},
		handleClipboardText(e, data) {
			this.evalutionText = data.replace(/\n/g, '');
			if (data) {
				this.operateType = 'hasPrint';
				let sdQuestId = snowflake.nextId().toString();
				this.SET_ANS_ID(sdQuestId);
				this.$ls.set('questId', sdQuestId);
				this.questId = sdQuestId;
				this.que_CPTEXT_SEND(
					JSON.stringify({
						csId: this.formData.csId,
						sdQuestId: this.questId,
						txtValue: this.evalutionText,
					})
				);
			} else {
				this.operateType = 'nostart';
				this.SET_isAnswering(0);
			}
		},
		playStuAudio(info) {
			if (info.play) {
				info.play = false;
				this.$refs.audioRef2.pause();
			} else {
				info.play = true;
				this.audioUrl2 = this.baseUrl + info.bid + '/a.mp3';
				this.$refs.audioRef2.load();
				this.$nextTick(() => {
					this.$refs.audioRef2.play();
				});
			}
		},
	},
};
</script>

<style scoped lang="less">
.stopbox {
	position: relative;
	.stopBtn,
	.resultBtn {
		width: 80px;
		background: #e43636;
		color: #fff;
		border-radius: 50px;
		height: 40px;
		text-align: center;
		line-height: 40px;
		font-size: 16px;
		font-weight: blod;
		cursor: pointer;
	}
	.resultBtn {
		background: #ffea65;
		color: #333333;
		margin-left: 10px;
	}
}
.list .item {
	background: #f3f8ff;
	margin: 10px;
	text-align: center;
	padding: 30px 20px 10px;
	position: relative;
	.rank {
		position: absolute;
		width: 34px;
		height: 34px;
		top: 0;
		left: 20px;
		color: #fff;
		background: url(/rank/rank6.png) no-repeat center center;
		background-size: auto;
		&.rank1 {
			background-image: url(/rank/rank1.png);
		}
		&.rank2 {
			background-image: url(/rank/rank2.png);
		}
		&.rank3 {
			background-image: url(/rank/rank3.png);
		}
		&.rank4 {
			background-image: url(/rank/rank4.png);
		}
		&.rank5 {
			background-image: url(/rank/rank5.png);
		}
	}
	.play {
		position: absolute;
		right: 0;
		top: 5px;
		width: 30px;
		cursor: pointer;
	}
}
.score {
	margin: 10px auto;
	/deep/ .ant-rate {
		font-size: 16px;
		.ant-rate-star {
			color: #ff3f3e;
			&:not(:last-child) {
				margin-right: 4px;
			}
		}
	}
}
.btn {
	position: relative;
	height: 60px;
	width: 60px;
	cursor: pointer;
	span {
		position: absolute;
		color: #fff;
		font-size: 12px;
		top: 50%;
		left: 50%;
		transform: translate(-35%, -50%);
		white-space: nowrap;
	}
}
</style>
<style>
@keyframes yuying1 {
	0% {
		height: 0%;
	}

	20% {
		height: 50%;
	}

	50% {
		height: 100%;
	}

	80% {
		height: 50%;
	}

	100% {
		height: 0%;
	}
}

#audio {
	width: 70px;
	height: 70px;
	left: 50%;
	text-align: center;
	background: #00bec5;
	border-radius: 100%;
	display: flex;
	align-items: center;
	justify-content: center;
	cursor: pointer;
}

#audio > div.audiobox {
	position: absolute;
	top: 10px;
	width: 100%;
	height: 30px;
	text-align: center;
	z-index: 1;
}

#audio > img {
	width: 30px;
	display: inline-block;
}
#audio #one {
	animation: yuying1 0.8s infinite 0.1s;
	-webkit-animation: yuying1 0.6s infinite 0.1s;
}

#audio #two {
	animation: yuying1 0.8s infinite 0.2s;
	-webkit-animation: yuying1 0.6s infinite 0.2s;
}

#audio #three {
	animation: yuying1 0.8s infinite 0.3s;
	-webkit-animation: yuying1 0.6s infinite 0.3s;
}

#audio #four {
	animation: yuying1 0.8s infinite 0.4s;
	-webkit-animation: yuying1 0.6s infinite 0.4s;
}

#audio #five {
	animation: yuying1 0.8s infinite 0.5s;
	-webkit-animation: yuying1 0.6s infinite 0.5s;
}

#audio #one,
#audio #two,
#audio #three,
#audio #four,
#audio #five {
	width: 3px;
	height: 100%;
	margin-left: 6px;
	border-radius: 50px;
	background-color: #fff;
	vertical-align: middle;
	display: inline-block;
}
</style>
